#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/5/8 12:44
# @Author  : LiShan
# @Email   : lishan_1997@126.com
# @File    : timming_random.py
# @Note    : this is note
import os
import threading
import time
import psutil
from winreg import OpenKey, QueryValueEx, HKEY_CURRENT_USER
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import torch
import agent.dqn as ag
from environment.vissim import VisCom
font = {'family' : 'SimSun',
    'weight' : 'bold',
    'size'  : '16'}
plt.rc('font', **font)
plt.rc('axes',unicode_minus=False)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
ZEROREWARD = 58
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
net_path =  project_path.replace("\\", "/") + '/resource/vissim/3.inp'
simulation = [999999, 0, 1, 1, 42, False, False]
timming = [26, 32, 2, 130, 20, 3, [2, 2, 2, 2]]
result_file = './txt/test_random_record.txt'
plans_file = './txt/test_random_plans.txt'
save_img = "./png/bar_random_delay.png"
program_name = 'run vissim.bat'
random_test = 20
test_step = 100


def get_desktop_path():
    key = OpenKey(HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
    return QueryValueEx(key, "Desktop")[0]


def detect_vissim():
    pids = psutil.pids()
    for pid in pids:
        p = psutil.Process(pid)
        process_name = p.name()
        if "vissim" in process_name:
            return True
    else:
        return False


def normalization_process(observation):
    """flow + speed + queue"""
    # state = observation
    flow = list(map(lambda x: round((x - 0) / (25 - 0), 2), observation[0:8]))
    speed = list(map(lambda x: round((x - 0) / (60 - 0), 2), observation[8:16]))
    queue = list(map(lambda x: round((x - 0) / (400 - 0), 2), observation[16:24]))
    state = flow + speed + queue
    return state


def get_reward_1(delay):
    if delay < ZEROREWARD - 10:
        return 10
    elif delay < ZEROREWARD - 9:
        return 9
    elif delay < ZEROREWARD - 8:
        return 8
    elif delay < ZEROREWARD - 7:
        return 7
    elif delay < ZEROREWARD - 6:
        return 6
    elif delay < ZEROREWARD - 5:
        return 5
    elif delay < ZEROREWARD - 4:
        return 4
    elif delay < ZEROREWARD - 3:
        return 3
    elif delay < ZEROREWARD - 2:
        return 2
    elif delay < ZEROREWARD - 1:
        return 1
    elif delay < ZEROREWARD:
        return 0
    elif delay < ZEROREWARD - (-1):
        return -1
    elif delay < ZEROREWARD - (-2):
        return -2
    elif delay < ZEROREWARD - (-3):
        return -3
    elif delay < ZEROREWARD - (-4):
        return -4
    elif delay < ZEROREWARD - (-5):
        return -5
    elif delay < ZEROREWARD - (-6):
        return -6
    elif delay < ZEROREWARD - (-7):
        return -7
    elif delay < ZEROREWARD - (-8):
        return -8
    elif delay < ZEROREWARD - (-9):
        return -9
    else:
        return -10


def get_reward_2(delay):
       return round(ZEROREWARD - delay, 2)


def create_plans(timming):
    # 配时范围
    green_low = timming[0]
    green_high = timming[1]
    green_interval = timming[2]
    cycle_time = timming[3]
    loss_time = timming[4]
    amber_time = timming[5]
    clearing_time = timming[6]
    plans = []
    try:
        with open(plans_file, "a+") as f:
            f.truncate(0)
    except (IndexError, Exception):
        pass
    for a in range(green_low, green_high + 1, green_interval):
        for b in range(green_low, green_high + 1, green_interval):
            for c in range(green_low, green_high + 1, green_interval):
                for d in range(green_low, green_high + 1, green_interval):
                    if (a + b + c + d) == (cycle_time - loss_time):
                        plan = [cycle_time, amber_time, clearing_time, [a, b, c, d]]
                        plans.append(plan)
                        with open(plans_file, "a+") as f:
                            line = "%s\n" % (str(plan))
                            f.write(line)
    return plans


def start_vissim():
    # # 获取vissim程序文件路径
    vissim_program_path = program_name
    # # 检测vissim是否运行
    # flag = detect_vissim()
    # if flag:
    #     os.system("taskkill /F /IM vissim.exe")
    #     time.sleep(3)
    # # 自动化启动vissim软件
    # start_vissim_application = threading.Thread(target=os.startfile, args=(vissim_program_path,))
    # start_vissim_application.daemon = 1
    # start_vissim_application.start()
    # while True:
    #     flag = detect_vissim()
    #     if flag:
    #         time.sleep(3)
    #         break
    #     else:
    #         time.sleep(1)
    #         continue

    # 创建配时方案
    plans = create_plans(timming)
    # 初始化vissim环境
    while True:
        try:
            env = VisCom(net_path, vissim_program_path, simulation, plans)
            break
        except:
            print("连接失败")
            continue
    ag.N_STATES = env.observation_space.shape[0]
    ag.N_ACTIONS = env.action_space.n
    if isinstance(env.action_space.sample(), int):
        ag.ENV_A_SHAPE = 0
    else:
        ag.ENV_A_SHAPE = env.action_space.sample().shape
    return env, plans


def test_random_timming(env, plans):
    print("开始测试......")

    try:
        with open(result_file, "a+") as f:
            f.truncate(0)
    except (IndexError, Exception):
        pass

    for plan in range(random_test):
        # 启动时间
        test_start = time.perf_counter()
        # 初始化参数
        test_delay_record = []
        test_rewrad_record = []
        # 可视化visism仿真图像
        # env.render()
        # 重置环境获取初始交通流状态
        observation = env.reset()
        # 热身时间
        for i in range(5):
            observation, reward, done, info = env.step(env.action_space.sample())

        try:
            with open(result_file[:-4] + '_' + str(plan) + '.txt', "a+") as f:
                f.truncate(0)
        except (IndexError, Exception):
            pass

        # 仿真运行指定个周期
        for step in range(test_step):
            progress = int((step + 1) / test_step * 100)
            print("\r正在测试随机配时方案" + str(plan) + ":" + "|" + "#" * progress + " " * (100 - progress) + "|"
                  + str(progress) + "%", end="")

            # 生成随机动作
            action = np.random.randint(0, len(plans))

            # vissim环境采取随机动作运行一周期，得到下一周期的状态信息
            observation, reward, done, info = env.step(action)
            state = normalization_process(observation)

            # 重定义奖励
            delay = reward
            redefine_reward = get_reward_2(delay)

            test_delay_record.append(delay)
            test_rewrad_record.append(redefine_reward)

            # 保存测试记录信息
            with open(result_file[:-4] + '_' + str(plan) + '.txt', 'a+') as f:
                record = "%s\t%s\t%s\t%s\t%s\t\n" % \
                         (str(step), str(action), str(delay), str(redefine_reward), str(state))
                f.write(record)

        # 输出网络的测试奖励值和延误值
        test_mean_delay = sum(test_delay_record)/len(test_delay_record)
        test_mean_reward = sum(test_rewrad_record)/len(test_rewrad_record)
        info = '\ntest plan: {}, test_mean_reward: {}, test_mean_delay: {}'. \
            format(plan, round(test_mean_reward, 3), round(test_mean_delay, 3))
        print(info)

        # 保存测试记录信息
        with open(result_file, 'a+') as f:
            record = "%s\t%s\t%s\t%s\t\n" % \
                     (str(plan), str(test_step), str(round(test_mean_delay, 3)), str(round(test_mean_reward, 3)))
            f.write(record)

        # 输出测试时间
        test_time = time.perf_counter() - test_start
        h, ss = divmod(test_time, 3600)
        m, s = divmod(ss, 60)
        info = "complete test time: {} second, that is {} hour, {} minute, {} second".format(test_time, h, m, s)
        print(info)


def draw_result():
    names = ["plan", "step", "delay", "reward"]
    data = pd.read_csv(result_file, error_bad_lines=False, sep="\s+", names=names)
    plan = list(data["plan"].values)
    step = list(data["step"].values)
    delay = list(data["delay"].values)
    reward = list(data["reward"].values)
    """延误"""
    min_idx = delay.index(min(delay))
    max_idx = delay.index(max(delay))
    # 绘图
    x = np.linspace(0, len(delay)-1, len(delay))
    rects = plt.bar(x, delay, 0.5, label ='other delay plan', color="w",edgecolor="k")
    plt.bar(min_idx, min(delay), 0.5, label ='min delay plan', color="w",edgecolor="k", hatch="\\\\\\\\")
    plt.bar(max_idx, max(delay), 0.5, label ='max delay plan', color="w",edgecolor="k", hatch="/////")
    # 在条形图上加标注（水平居中）
    print(min_idx, max_idx)
    for i in range(1, len(rects)):
        if(i==min_idx or i==max_idx):
            rect = rects[i]
            height = rect.get_height()
            plt.text(rect.get_x() + rect.get_width() / 2, height + 0.3, str(round(height, 2)), ha='center',
                     fontproperties="Times New Roman", size=8.5)
    # 设置坐标轴范围
    plt.xlim([-1, 20])
    plt.ylim([50, 70])
    # 设置坐标轴刻度
    plt.xticks(range(0, 20, 1))
    plt.yticks(np.arange(50, 72, 2))
    # 设置坐标轴名称
    plt.xlabel("随机配时方案", fontproperties="SimSun", size=10.5)
    plt.ylabel("1000步平均延误时间(s)", fontproperties="SimSun", size=10.5)
    # 设置网格
    # plt.grid()
    # 设置边框
    # plt.axis('off')
    # 设置图例
    legend = ["other delay plan", "min delay plan", "max delay plan"]
    plt.legend(legend, loc="best", frameon=False)
    # 设置标题
    # plt.title("The Delay Curve", fontproperties="Times New Roman", size=10.5)
    # 保存图片
    plt.savefig(save_img, dpi=600)
    # 关闭绘图
    plt.close()
    print("绘图完成...")


if __name__ == '__main__':
    path_list = ['./pkl', './png', './txt']
    for path in path_list:
        if os.path.exists(path):
            pass
        else:
            os.mkdir(path)
    env, plans = start_vissim()
    test_random_timming(env, plans)
    draw_result()
